#include "SDLGo.h"
#include "../core/Logger.h"
#include "../core/Dispatch.h"
#include "../core/InterData.h"
//#include "SDL_opengl.h"
//#include "SDL_image.h"
//#include "SDL_net.h"


std::map<SDLKey, std::string> produceSDLTransTable()
{
        std::map<SDLKey, std::string> ret;
        ret[SDLK_BACKSPACE] = "BACKSPACE";
        ret[SDLK_TAB] = "TAB";
        ret[SDLK_CLEAR] = "CLEAR";
        ret[SDLK_RETURN] = "RETURN";
        ret[SDLK_PAUSE] = "PAUSE";
        ret[SDLK_ESCAPE] = "ESCAPE";
        ret[SDLK_SPACE] = "SPACE";
        ret[SDLK_EXCLAIM] = "EXCLAIM";
        ret[SDLK_QUOTEDBL] = "QUOTEDBL";
        ret[SDLK_HASH] = "HASH";
        ret[SDLK_DOLLAR] = "DOLLAR";
        ret[SDLK_AMPERSAND] = "AMPERSAND";
        ret[SDLK_QUOTE] = "QUOTE";
        ret[SDLK_LEFTPAREN] = "LEFTPAREN";
        ret[SDLK_RIGHTPAREN] = "RIGHTPAREN";
        ret[SDLK_ASTERISK] = "ASTERISK";
        ret[SDLK_PLUS] = "PLUS";
        ret[SDLK_COMMA] = "COMMA";
        ret[SDLK_MINUS] = "MINUS";
        ret[SDLK_PERIOD] = "PERIOD";
        ret[SDLK_SLASH] = "SLASH";
        ret[SDLK_0] = "0";
        ret[SDLK_1] = "1";
        ret[SDLK_2] = "2";
        ret[SDLK_3] = "3";
        ret[SDLK_4] = "4";
        ret[SDLK_5] = "5";
        ret[SDLK_6] = "6";
        ret[SDLK_7] = "7";
        ret[SDLK_8] = "8";
        ret[SDLK_9] = "9";
        ret[SDLK_COLON] = "COLON";
        ret[SDLK_SEMICOLON] = "SEMICOLON";
        ret[SDLK_LESS] = "LESS";
        ret[SDLK_EQUALS] = "EQUALS";
        ret[SDLK_GREATER] = "GREATER";
        ret[SDLK_QUESTION] = "QUESTION";
        ret[SDLK_AT] = "AT";
        ret[SDLK_LEFTBRACKET] = "LEFTBRACKET";
        ret[SDLK_BACKSLASH] = "BACKSLASH";
        ret[SDLK_RIGHTBRACKET] = "RIGHTBRACKET";
        ret[SDLK_CARET] = "CARET";
        ret[SDLK_UNDERSCORE] = "UNDERSCORE";
        ret[SDLK_BACKQUOTE] = "BACKQUOTE";
        ret[SDLK_a] = "a";
        ret[SDLK_b] = "b";
        ret[SDLK_c] = "c";
        ret[SDLK_d] = "d";
        ret[SDLK_e] = "e";
        ret[SDLK_f] = "f";
        ret[SDLK_g] = "g";
        ret[SDLK_h] = "h";
        ret[SDLK_i] = "i";
        ret[SDLK_j] = "j";
        ret[SDLK_k] = "k";
        ret[SDLK_l] = "l";
        ret[SDLK_m] = "m";
        ret[SDLK_n] = "n";
        ret[SDLK_o] = "o";
        ret[SDLK_p] = "p";
        ret[SDLK_q] = "q";
        ret[SDLK_r] = "r";
        ret[SDLK_s] = "s";
        ret[SDLK_t] = "t";
        ret[SDLK_u] = "u";
        ret[SDLK_v] = "v";
        ret[SDLK_w] = "w";
        ret[SDLK_x] = "x";
        ret[SDLK_y] = "y";
        ret[SDLK_z] = "z";
        ret[SDLK_DELETE] = "DELETE";
        ret[SDLK_KP0] = "KP0";
        ret[SDLK_KP1] = "KP1";
        ret[SDLK_KP2] = "KP2";
        ret[SDLK_KP3] = "KP3";
        ret[SDLK_KP4] = "KP4";
        ret[SDLK_KP5] = "KP5";
        ret[SDLK_KP6] = "KP6";
        ret[SDLK_KP7] = "KP7";
        ret[SDLK_KP8] = "KP8";
        ret[SDLK_KP9] = "KP9";
        ret[SDLK_KP_PERIOD] = "KP_PERIOD";
        ret[SDLK_KP_DIVIDE] = "KP_DIVIDE";
        ret[SDLK_KP_MULTIPLY] = "KP_MULTIPLY";
        ret[SDLK_KP_MINUS] = "KP_MINUS";
        ret[SDLK_KP_PLUS] = "KP_PLUS";
        ret[SDLK_KP_ENTER] = "KP_ENTER";
        ret[SDLK_KP_EQUALS] = "KP_EQUALS";
        ret[SDLK_UP] = "UP";
        ret[SDLK_DOWN] = "DOWN";
        ret[SDLK_RIGHT] = "RIGHT";
        ret[SDLK_LEFT] = "LEFT";
        ret[SDLK_INSERT] = "INSERT";
        ret[SDLK_HOME] = "HOME";
        ret[SDLK_END] = "END";
        ret[SDLK_PAGEUP] = "PAGEUP";
        ret[SDLK_PAGEDOWN] = "PAGEDOWN";
        ret[SDLK_F1] = "F1";
        ret[SDLK_F2] = "F2";
        ret[SDLK_F3] = "F3";
        ret[SDLK_F4] = "F4";
        ret[SDLK_F5] = "F5";
        ret[SDLK_F6] = "F6";
        ret[SDLK_F7] = "F7";
        ret[SDLK_F8] = "F8";
        ret[SDLK_F9] = "F9";
        ret[SDLK_F10] = "F10";
        ret[SDLK_F11] = "F11";
        ret[SDLK_F12] = "F12";
        ret[SDLK_F13] = "F13";
        ret[SDLK_F14] = "F14";
        ret[SDLK_F15] = "F15";
        ret[SDLK_NUMLOCK] = "NUMLOCK";
        ret[SDLK_CAPSLOCK] = "CAPSLOCK";
        ret[SDLK_SCROLLOCK] = "SCROLLOCK";
        ret[SDLK_RSHIFT] = "RSHIFT";
        ret[SDLK_LSHIFT] = "LSHIFT";
        ret[SDLK_RCTRL] = "RCTRL";
        ret[SDLK_LCTRL] = "LCTRL";
        ret[SDLK_RALT] = "RALT";
        ret[SDLK_LALT] = "LALT";
        ret[SDLK_RMETA] = "RMETA";
        ret[SDLK_LMETA] = "LMETA";
        ret[SDLK_LSUPER] = "LSUPER";
        ret[SDLK_RSUPER] = "RSUPER";
        ret[SDLK_MODE] = "MODE";
        ret[SDLK_COMPOSE] = "COMPOSE";
        ret[SDLK_HELP] = "HELP";
        ret[SDLK_PRINT] = "PRINT";
        ret[SDLK_SYSREQ] = "SYSREQ";
        ret[SDLK_BREAK] = "BREAK";
        ret[SDLK_MENU] = "MENU";
        ret[SDLK_POWER] = "POWER";
        ret[SDLK_EURO] = "EURO";
        ret[SDLK_UNDO] = "UNDO";
        return ret;
}

std::string charConv(SDL_Event& e, std::map<SDLKey, std::string>& ct)
{
    // magic numbers courtesy of SDL docs :)
    const int INTERNATIONAL_MASK = 0xFF80, UNICODE_MASK = 0x7F;

    int uni = e.key.keysym.unicode;

    if( uni == 0 ) // not translatable key (like up or down arrows)
    {
        // probably not useful as string input
        // we could optionally use this to get some value
        // for it: SDL_GetKeyName( key );
        return ct[e.key.keysym.sym];
    }
    else if( ( uni & INTERNATIONAL_MASK ) == 0 ) 
        {
                std::string ret = "?";

        if( SDL_GetModState() & KMOD_SHIFT )
        {
            ret[0] = static_cast<char>(toupper(uni & UNICODE_MASK));
        }
        else
        {
            ret[0] = static_cast<char>(uni & UNICODE_MASK);
        }

                if(ret[0] < 32 || ret[0] >= 127)
                        return ct[e.key.keysym.sym];
                else
                        return ret;
    }
    else // we have a funky international character. one we can't read :(
    {
        // we could do nothing, or we can just show some sign of input, like so:
        // return '?';
        return "UNKNOWN_KEY";
    }
}

SDLGo::SDLGo(Core* c)
{
	_core = c;

	// SDL Initialization
	int error = -1;

	// ok, sound ought to be initted now as well
	error = SDL_Init(SDL_INIT_EVERYTHING);
	if (error < 0) 
		LOG(logERROR) << "SDL_Init failed in SDLGo constructor: " << error;

	SDL_EnableUNICODE(1);
	// probably we do not want key repeat, since it will make a lot of game interface
	// stuff more complicated
	//SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);

	//_tableMade = false;
	_convTable = produceSDLTransTable();
}

SDLGo::~SDLGo()
{
	// nothing to do
}

std::string SDLGo::getClassName()
{
	return "SDLGo";
}

void sendKeyEvent(DispatchGO* dgo, int keyID, bool keyDown, std::string& keyname)
{
	IData temp;
	temp.seti(temp.named("keyID"), keyID);
	temp.setu(temp.named("keyDown"), keyDown);
	temp.sets(temp.named("keyName"), keyname);
	dgo->sendMessage("event:keyEvent", temp);
}

void sendKeyTypedEvent(DispatchGO* dgo, char key, std::string& keystr)
{
	IData temp;
	temp.seti(temp.named("keyVal"), key);
	temp.sets(temp.named("keyName"), keystr);
	dgo->sendMessage("event:keyTyped", temp);
}

void sendMouseMoveEvent(DispatchGO* dgo, float x, float y, float dx, float dy)
{
	IData temp;
	temp.setf(temp.named("x"), x);
	temp.setf(temp.named("y"), y);
	temp.setf(temp.named("dx"), dx);
	temp.setf(temp.named("dy"), dy);
	dgo->sendMessage("event:mouseMove", temp);
}

void sendMouseButtonEvent(DispatchGO* dgo, int buttonID, bool buttonDown, float x, float y)
{
	IData temp;
	temp.seti(temp.named("buttonID"), buttonID);
	temp.setu(temp.named("buttonDown"), buttonDown);
	temp.setf(temp.named("x"), x);
	temp.setf(temp.named("y"), y);
	dgo->sendMessage("event:mouseButton", temp);
}

void SDLGo::updateFromUM(double dt)
{
	// get the dispatch object
	DispatchGO* dgo = NULL;

	GlobalObject* temp = _core->getGlobalObject("DispatchGO");
	if(temp != NULL)
	{
		dgo = dynamic_cast<DispatchGO*>(temp);
	}
	else
	{
		dgo = new DispatchGO;
		_core->addGlobalObject(dgo, "DispatchGO");
	}

	unsigned char keysymbol = 0;

	SDL_Event e;
	bool buttonDown;
	int buttonID;
	int bx, by, bdx, bdy;
	std::string keyt;

	while (SDL_PollEvent(&e))
	{
		switch(e.type) {
		case SDL_KEYDOWN:
			keyt = charConv(e, _convTable);
			if(keyt.size() == 1)
			{
				sendKeyTypedEvent(dgo, keyt[0], keyt);
			}
			sendKeyEvent(dgo, e.key.keysym.sym, true, _convTable[e.key.keysym.sym]);
			break;

		case SDL_KEYUP:
			keyt = charConv(e, _convTable);
			sendKeyEvent(dgo, e.key.keysym.sym, false, _convTable[e.key.keysym.sym]);
			break;

		case SDL_MOUSEBUTTONDOWN:
		case SDL_MOUSEBUTTONUP:
			buttonDown = (e.button.state == SDL_PRESSED);

			if(e.button.button == SDL_BUTTON_LEFT) {
				buttonID = 1;
			} else if(e.button.button == SDL_BUTTON_RIGHT) {
				buttonID = 2;
			} else if(e.button.button == SDL_BUTTON_MIDDLE) {
				buttonID = 3;
			} else if(e.button.button == SDL_BUTTON_WHEELUP) {
				buttonID = 4;
			} else if(e.button.button == SDL_BUTTON_WHEELDOWN) {
				buttonID = 5;
			} else {
				buttonID = 6;
			}

			bx = e.button.x;
			by = e.button.y;

			sendMouseButtonEvent(dgo, buttonID, buttonDown, (float)bx, (float)by);
			break;

		case SDL_MOUSEMOTION:
			bx = e.motion.x;
			by = e.motion.y;
			bdx = e.motion.xrel;
			bdy = e.motion.yrel;

			sendMouseMoveEvent(dgo, (float)bx, (float)by, (float)bdx, (float)bdy);
			break;

		case SDL_QUIT:
			exit(0);
			break;

		default:
			break;
		}   // End switch    
	} // End while
}